package com.chinacoal.microservice;


import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.chinacoal.microservice.mapper.CcmsSmartleasingDeviceManagementMapper;
import com.chinacoal.microservice.mapper.CcmsSmartleasingMessageMapper;
import com.chinacoal.microservice.pojo.BasicConfiguration;
import com.chinacoal.microservice.pojo.CcmsSmartleasingDeviceManagement;
import com.chinacoal.microservice.pojo.CcmsSmartleasingMessage;
import com.chinacoal.microservice.util.DESDZFP;
import com.chinacoal.microservice.util.HttpUtil;
import com.google.common.primitives.Chars;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.eclipse.paho.client.mqttv3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@Component
@Slf4j
public class ReadyApplicationListener implements ApplicationListener<ApplicationReadyEvent> {

    // W3QF1 has 9 alarms and 10 faults
    private static String[] W3QF1_alarms = new String[]{"过载报警", "短路报警", "欠压报警", "过压报警", "温度报警", "恶性负载报警", "过功率报警", "过用电量报警", "打火报警"};

    private static String[] W3QF1_faults = new String[]{"过载故障", "短路故障", "欠压故障", "过压故障", "高过压故障", "温度故障", "过功率故障", "过用电量故障", "恶性负载故障", "打火故障"};

    // W3RCB1 has 10 alarms and 12 faults
    private static String[] W3RCB1_alarms = new String[]{"过载报警", "短路报警", "漏电报警", "欠压报警", "过压报警", "温度报警", "过功率报警", "过用电量报警", "打火报警", "恶性负载报警"};

    private static String[] W3RCB1_faults = new String[]{"过载故障", "短路故障", "漏电故障", "欠压故障", "过压故障", "高过压故障", "温度故障", "硬件漏电故障", "过功率故障", "过用电量故障", "恶性负载故障", "打火故障"};


    // W3RCB3 HAS 29 alarms and 34 faults
    private static String[] W3RCB3_alarms = new String[]{"A相过载报警", "B相过载报警", "C相过载报警", "A相瞬时报警", "B相瞬时报警", "C相瞬时报警", "漏电报警", "A相欠压报警", "B相欠压报警", "C相欠压报警", "A相过压报警", "B相过压报警", "C相过压报警", "温度报警", "A相过功率报警", "B相过功率报警", "C相过功率报警", "A相恶性负载报警", "B相恶性负载报警", "C相恶性负载报警", "A相打火报警", "B相打火报警", "C相打火报警", "过用电量报警", "接地报警", "三相电压不平衡报警", "三相电流不平衡报警", "缺相报警", "相序错误报警"};

    private static String[] W3RCB3_faults = new String[]{"A相过载故障", "B相过载故障", "C相过载故障", "A相瞬时故障", "B相瞬时故障", "C相瞬时故障", "漏电故障", "A相欠压故障", "B相欠压故障", "C相欠压故障", "A相过压故障", "B相过压故障", "C相过压故障", "温度故障", "A相过功率故障", "B相过功率故障", "C相过功率故障", "A相恶性负载故障", "B相恶性负载故障", "C相恶性负载故障", "A相打火故障", "B相打火故障", "C相打火故障", "过用电量故障", "接地故障", "三相电压不平衡故障", "三相电流不平衡故障", "缺相故障", "相序错误故障", "A相高过压故障", "B相高过压故障", "硬件漏电故障", "C相高过压故障", "漏电试验故障"};

    @Autowired
    CcmsSmartleasingDeviceManagementMapper dmMapper;

    @Autowired
    CcmsSmartleasingMessageMapper msgMapper;

    @Autowired
    CcmsSmartleasingMessage msg;

    @Autowired
    CcmsSmartleasingDeviceManagement dm;
    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    BasicConfiguration basicConfiguration;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {

        String weiyuToken;
        String weiyuHTTPUrl = basicConfiguration.getWeiyuHTTPUrl();
        Object tmp = redisTemplate.opsForValue().get("weiyuToken");
        if (tmp == null) {
            //get token from http server,and assign it to the weiyuToken in redis
            weiyuToken = HttpUtil.getWeiyuToken(weiyuHTTPUrl + "/v3/Authorizations", basicConfiguration.getWeiyuUname(), basicConfiguration.getWeiyuPwd());
            redisTemplate.opsForValue().set("weiyuToken", weiyuToken);
        } else
            weiyuToken = tmp.toString();

        String sAppId = Convert.toStr(redisTemplate.opsForValue().get("accessId"), "");
        String sAccessKey = Convert.toStr(redisTemplate.opsForValue().get("accessKey"), "");
        String sSecretKey = Convert.toStr(redisTemplate.opsForValue().get("secretKey"), "");
        String sInstanceId = Convert.toStr(redisTemplate.opsForValue().get("instanceId"), "");
        Integer sResponseCode = 0;
        JSONObject oResponse = null;

        if (sAppId.equals("") || sAccessKey.equals("") || sSecretKey.equals("") || sInstanceId.equals("")) {
            String strResponse = HttpUtil.getWeiyuMQTTParams(weiyuHTTPUrl + "/v4/user/getMQTTKeys", weiyuToken);
            if (!strResponse.equals("")) {
                oResponse = JSON.parseObject(strResponse);
                sResponseCode = oResponse.getInteger("code");
            }

            if (sResponseCode == 200) {
                JSONObject oData = oResponse.getJSONObject("data");
                sAppId = oData.getString("appId");
                sAccessKey = oData.getString("accessKey");
                sSecretKey = oData.getString("secretKey");
                sInstanceId = oData.getString("instanceId");
            }
        }

        if (!sAppId.equals("") && !sAccessKey.equals("") && !sSecretKey.equals("") && !sInstanceId.equals("")) {
            try {
                mqttConn_Subscribe(sAppId, sAccessKey, sSecretKey, sInstanceId);
            } catch (MqttException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private void mqttConn_Subscribe(String sAppId, String sAccessKey, String sSecretKey, String sInstanceId) throws MqttException {
        log.info("mqttconnect..." + sAppId + "," + sAccessKey + "," + sSecretKey + "," + sInstanceId);
        String mqttUrl = basicConfiguration.getWeiyuMQTTUrl();
        String sClientId = "GID_PARTY3@@@" + sAppId + "_11"; // 11 is self-specified
        String uname = "Signature|" + sAccessKey + "|" + sInstanceId;
        String pwd = "";
        try {
            pwd = DESDZFP.macSignature(sClientId, sSecretKey);
        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
            e.printStackTrace();
        }

//        MqttClient mqttClient = new MqttClient("tcp://mqtt.etor.top:1883",sClientId);
        MqttClient mqttClient = new MqttClient(basicConfiguration.getWeiyuMQTTUrl(), sClientId);

        MqttConnectOptions options = new MqttConnectOptions();
        options.setUserName(uname);
        options.setPassword(pwd.toCharArray());
        options.setConnectionTimeout(5); //设置超时时间
        options.setCleanSession(true);
        options.setKeepAliveInterval(60);//设置会话心跳时间
        options.setAutomaticReconnect(true); //自动重连
        mqttClient.setCallback(new WeiyuMQTTCallback());
        mqttClient.connect(options);

        String[] sTopics = {sAppId + "/out/deviceState"};
        int qos[] = new int[1];
        mqttClient.subscribe(sTopics, qos);
    }

    class WeiyuMQTTCallback implements MqttCallback {
        // public static void main(String[] args) {
        //     String dt = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(new java.util.Date(1668431798000L));
        //            System.out.println(dt);
        //        }

        /**
         * 从 alarm 或 fault 的字段中解析出报警或故障的详情
         */
        private String parseAlarm(String model, Integer alarmValue) {
            List<Character> bits = Chars.asList(Integer.toBinaryString(alarmValue).toCharArray());
            Collections.reverse(bits);
            String[] saAlarms = {};
            String sResult = "";

            if (model == "W3QF1")
                saAlarms = W3QF1_alarms;
            else if (model == "W3RCB1")
                saAlarms = W3RCB1_alarms;
            else if (model == "W3RCB3")
                saAlarms = W3RCB3_alarms;
            for (int i = 0; i < bits.size(); i++) {
                if (bits.get(i) == '1')
                    sResult += saAlarms[i];
            }
            return sResult;
        }

        private String parseFault(String model, Integer faultValue) {
            List<Character> bits = Chars.asList(Integer.toBinaryString(faultValue).toCharArray());
            Collections.reverse(bits);
            String[] saFaults = {};
            String sResult = "";

            if (model == "W3QF1")
                saFaults = W3QF1_faults;
            else if (model == "W3RCB1")
                saFaults = W3RCB1_faults;
            else if (model == "W3RCB3")
                saFaults = W3RCB3_faults;
            for (int i = 0; i < bits.size(); i++) {
                if (bits.get(i) == '1')
                    sResult += saFaults[i] + ",";
            }
            return sResult;
        }

        @Override
        public void messageArrived(String topic, MqttMessage message) throws Exception {
//            log.info("===MQTT-Client 接收消息主题 : " + topic);
//            log.info("===MQTT-Client 接收消息Qos : " + message.getQos());
            String sPayLoad = new String(message.getPayload());
//            log.info("===MQTT-Client 接收消息内容 : " + sPayLoad);

            JSONObject jsoPayLoad = JSON.parseObject(sPayLoad);
            String bizCode = jsoPayLoad.getString("bizCode");

            JSONObject jsoBizData = jsoPayLoad.getJSONObject("bizData");
            Long lAlarmTime = jsoBizData.getLong("time") * 1000L;
            Date dtAlarmTime = new Date(lAlarmTime);
            String sDevNo = jsoBizData.getString("devno"), sFinal = "";

            //作兼容性处理
            if (StringUtils.isBlank(sDevNo)) {
                return;
            }

            String sDmPkID = dmMapper.serialNumberUniqueness(sDevNo);
            if (StringUtils.isBlank(sDmPkID)) {
                return;
            }

            Date dtNow = new Date();

            // insert online/offline msg into DB and redis
            if (bizCode == "offline" || bizCode == "online") {
                // 0=ok, 1 = not ok
                sFinal = bizCode;
                Byte bStatus = bizCode.equals("online") ? new Byte("0") : new Byte("1");
                dmMapper.updateDeviceStatus(new Date(), 0, bStatus, sDevNo);
            } else { // 3 scenario: both alarm and fault occur, alarm occur, fault occur,.....
                // besides the 3, "neither occur" record status in DM table, redis, while not in msg table;
                Object oAlarm = jsoBizData.get("alarm");
                Object oFault = jsoBizData.get("fault");
                Integer iAlarm = 0, iFault = 0;
                String sAlarm = "", sFault = "", sCategory = ""; //category = model
                sCategory = jsoBizData.getString("category");

                if (oAlarm.equals(null) && oFault.equals(null)) {
                    dmMapper.updateDeviceStatus(dtNow, 0, new Byte("0"), sDevNo);
                    redisTemplate.opsForValue().set("weiyuStatus:" + sDevNo, "running ok");
                    return;
                } else if (!oAlarm.equals(null) && !oFault.equals(null)) {
                    iAlarm = jsoBizData.getInteger("alarm");
                    iFault = jsoBizData.getInteger("fault");
                } else if (!oAlarm.equals(null))
                    iAlarm = jsoBizData.getInteger("alarm");
                else if (!oFault.equals(null))
                    iFault = jsoBizData.getInteger("fault");

                if (iAlarm == 0 & iFault ==0){
                    dmMapper.updateDeviceStatus(dtNow, 0, new Byte("0"), sDevNo);
                    redisTemplate.opsForValue().set("weiyuStatus:" + sDevNo, "运行正常_running ok");
                    return;
                } else if (iAlarm != 0 & iFault !=0) {
                    sAlarm = parseAlarm(sCategory, iAlarm);
                    sFault = parseFault(sCategory, iFault);
                    sFinal = "alarm=" + sAlarm + "====,fault=" + sFault;
                    dmMapper.updateDeviceStatus(dtNow, 0, new Byte("3"), sDevNo);
                } else if (iAlarm !=0) {
                    sAlarm = parseAlarm(sCategory,iAlarm);
                    sFinal = "alarm=" + sAlarm ;
                    dmMapper.updateDeviceStatus(dtNow, 0, new Byte("2"), sDevNo);
                } else if (iFault !=0) {
                    sFault = parseFault(sCategory, iFault);
                    sFinal = "fault=" + sFault;
                    dmMapper.updateDeviceStatus(dtNow, 0, new Byte("1"), sDevNo);
                }
            }

            redisTemplate.opsForValue().set("weiyuStatus:" + sDevNo, sFinal);
            msg.setPkMessageId(UUID.randomUUID() + "")
                    .setFkDeviceManagementId(sDmPkID)
                    .setCreatedBy(0)
                    .setCreationDate(dtNow)
                    .setLastUpdateDate(dtNow)
                    .setLastUpdatedBy(0)
                    .setAlarm(sFinal)
                    .setAlarmTime(dtAlarmTime);
            msgMapper.insert(msg);
        }

        @Override
        public void deliveryComplete(IMqttDeliveryToken token) {

        }


        @Override
        public void connectionLost(Throwable cause) {

        }
    }
}